Skip to content

Conversation

@mikesposito
Copy link
Member

@mikesposito mikesposito commented Nov 12, 2025

Explanation

This PR extracts the second and final set of changes from #5963, which refactors the KeyringController to always derive the encryption key from the password, rather than storing it in memory.

This change allows to simplify #unlockKeyrings and #updateVault methods, after the removeal of all the logic and tests related to cacheEncryptionKey. This also allows to remove this.#password, that has been replaced by this.#encryptionKey.

The this.#encryptionKey assignment logic has been moved to two new internal methods with these specific responsibilities:

  • #deriveEncryptionKey(string): Derives the encryption key from the password, to be used during password login and password change.
  • #useEncryptionKey(string, string): Uses an existing encryption key to be used directly, to be used by submitEncryptionKey mainly.

Package previews from this branch are tested on clients with these PRs:

References

Checklist

  • I've updated the test suite for new or updated code as appropriate
  • I've updated documentation (JSDoc, Markdown, etc.) for new or updated code as appropriate
  • I've communicated my changes to consumers by updating changelogs for packages I've changed, highlighting breaking changes as necessary
  • I've prepared draft pull requests for clients and consumer packages to resolve any breaking changes

Note

KeyringController now derives and uses an exportable encryption key (with salt) instead of storing the password, updates method flows and error handling, and requires encryptors to implement exportKey/keyFromPassword/generateSalt; dependencies and tests updated accordingly.

  • Keyring Controller:
    • Always derives and uses serialized encryptionKey + salt; removes in-memory password usage and persists vault via encryptWithKey.
    • Refactors unlock/reencrypt flows: submitPassword, submitEncryptionKey, changePassword, and vault upgrades now use derived/exported keys.
    • Adds internal helpers #deriveAndSetEncryptionKey and #setEncryptionKey; updates rollback/persist logic to track encryption credentials.
    • Introduces WrongEncryptionKeyType error; improves validation and error paths (e.g., missing/expired credentials, vault shape).
    • Updates Encryptor interface (BREAKING): requires exportKey, keyFromPassword, and generateSalt.
    • Test suite overhauled for new flows; coverage thresholds tightened.
  • Seedless Onboarding Controller:
    • Notes BREAKING requirement: provided encryptor must implement exportKey, keyFromPassword, generateSalt.
  • Dependencies:
    • Bump @metamask/browser-passworder to ^6.0.0 in affected packages.

Written by Cursor Bugbot for commit 71fc196. This will update automatically on new commits. Configure here.

@mikesposito
Copy link
Member Author

@metamaskbot publish-preview

@github-actions
Copy link
Contributor

Preview builds have been published. See these instructions for more information about preview builds.

Expand for full list of packages and versions.
{
  "@metamask-previews/account-tree-controller": "3.0.0-preview-cb2e5c04",
  "@metamask-previews/accounts-controller": "34.0.0-preview-cb2e5c04",
  "@metamask-previews/address-book-controller": "7.0.0-preview-cb2e5c04",
  "@metamask-previews/analytics-controller": "0.0.0-preview-cb2e5c04",
  "@metamask-previews/announcement-controller": "8.0.0-preview-cb2e5c04",
  "@metamask-previews/app-metadata-controller": "2.0.0-preview-cb2e5c04",
  "@metamask-previews/approval-controller": "8.0.0-preview-cb2e5c04",
  "@metamask-previews/assets-controllers": "88.0.0-preview-cb2e5c04",
  "@metamask-previews/base-controller": "9.0.0-preview-cb2e5c04",
  "@metamask-previews/bridge-controller": "60.0.0-preview-cb2e5c04",
  "@metamask-previews/bridge-status-controller": "60.0.0-preview-cb2e5c04",
  "@metamask-previews/build-utils": "3.0.4-preview-cb2e5c04",
  "@metamask-previews/chain-agnostic-permission": "1.2.2-preview-cb2e5c04",
  "@metamask-previews/claims-controller": "0.2.0-preview-cb2e5c04",
  "@metamask-previews/composable-controller": "12.0.0-preview-cb2e5c04",
  "@metamask-previews/controller-utils": "11.15.0-preview-cb2e5c04",
  "@metamask-previews/core-backend": "4.0.0-preview-cb2e5c04",
  "@metamask-previews/delegation-controller": "1.0.0-preview-cb2e5c04",
  "@metamask-previews/earn-controller": "10.0.0-preview-cb2e5c04",
  "@metamask-previews/eip-5792-middleware": "2.0.0-preview-cb2e5c04",
  "@metamask-previews/eip-7702-internal-rpc-middleware": "0.1.0-preview-cb2e5c04",
  "@metamask-previews/eip1193-permission-middleware": "1.0.2-preview-cb2e5c04",
  "@metamask-previews/ens-controller": "18.0.0-preview-cb2e5c04",
  "@metamask-previews/error-reporting-service": "3.0.0-preview-cb2e5c04",
  "@metamask-previews/eth-block-tracker": "14.0.0-preview-cb2e5c04",
  "@metamask-previews/eth-json-rpc-middleware": "21.0.0-preview-cb2e5c04",
  "@metamask-previews/eth-json-rpc-provider": "5.0.1-preview-cb2e5c04",
  "@metamask-previews/foundryup": "1.0.1-preview-cb2e5c04",
  "@metamask-previews/gas-fee-controller": "25.0.0-preview-cb2e5c04",
  "@metamask-previews/gator-permissions-controller": "0.4.0-preview-cb2e5c04",
  "@metamask-previews/json-rpc-engine": "10.1.1-preview-cb2e5c04",
  "@metamask-previews/json-rpc-middleware-stream": "8.0.8-preview-cb2e5c04",
  "@metamask-previews/keyring-controller": "24.0.0-preview-cb2e5c04",
  "@metamask-previews/logging-controller": "7.0.0-preview-cb2e5c04",
  "@metamask-previews/message-manager": "14.0.0-preview-cb2e5c04",
  "@metamask-previews/messenger": "0.3.0-preview-cb2e5c04",
  "@metamask-previews/multichain-account-service": "3.0.0-preview-cb2e5c04",
  "@metamask-previews/multichain-api-middleware": "1.2.4-preview-cb2e5c04",
  "@metamask-previews/multichain-network-controller": "2.0.0-preview-cb2e5c04",
  "@metamask-previews/multichain-transactions-controller": "6.0.0-preview-cb2e5c04",
  "@metamask-previews/name-controller": "9.0.0-preview-cb2e5c04",
  "@metamask-previews/network-controller": "25.0.0-preview-cb2e5c04",
  "@metamask-previews/network-enablement-controller": "3.1.0-preview-cb2e5c04",
  "@metamask-previews/notification-services-controller": "19.0.0-preview-cb2e5c04",
  "@metamask-previews/permission-controller": "12.1.0-preview-cb2e5c04",
  "@metamask-previews/permission-log-controller": "5.0.0-preview-cb2e5c04",
  "@metamask-previews/phishing-controller": "15.0.0-preview-cb2e5c04",
  "@metamask-previews/polling-controller": "15.0.0-preview-cb2e5c04",
  "@metamask-previews/preferences-controller": "21.0.0-preview-cb2e5c04",
  "@metamask-previews/profile-sync-controller": "26.0.0-preview-cb2e5c04",
  "@metamask-previews/rate-limit-controller": "7.0.0-preview-cb2e5c04",
  "@metamask-previews/remote-feature-flag-controller": "2.0.0-preview-cb2e5c04",
  "@metamask-previews/sample-controllers": "3.0.0-preview-cb2e5c04",
  "@metamask-previews/seedless-onboarding-controller": "6.1.0-preview-cb2e5c04",
  "@metamask-previews/selected-network-controller": "25.0.0-preview-cb2e5c04",
  "@metamask-previews/shield-controller": "2.0.0-preview-cb2e5c04",
  "@metamask-previews/signature-controller": "36.0.0-preview-cb2e5c04",
  "@metamask-previews/subscription-controller": "4.0.0-preview-cb2e5c04",
  "@metamask-previews/token-search-discovery-controller": "4.0.0-preview-cb2e5c04",
  "@metamask-previews/transaction-controller": "61.1.0-preview-cb2e5c04",
  "@metamask-previews/transaction-pay-controller": "4.0.0-preview-cb2e5c04",
  "@metamask-previews/user-operation-controller": "40.0.0-preview-cb2e5c04"
}

@mikesposito mikesposito force-pushed the mikesposito/keyring-controller/never-use-password branch from 8f04566 to ad1ae0d Compare November 12, 2025 10:22
state.encryptionSalt = updatedState.encryptionSalt;
}
state.encryptionKey = encryptionKey;
state.encryptionSalt = parsedEncryptedVault.salt;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there is a potential bug here:
Above you set

        this.#setEncryptionKey(
          credentials.encryptionKey,
          credentials.encryptionSalt || parsedEncryptedVault.salt,
        );

but here you set

state.encryptionSalt = parsedEncryptedVault.salt;

Shouldn't this be:

state.encryptionSalt = this.#encryptionKey.salt;

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As we are decrypting the vault here, the existing vault salt and this.#encryptionKey.salt should always be the same, even when the salt is undefined, right?

Copy link
Member

@Gudahtt Gudahtt Nov 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, we have a check to ensure they are the same in #setEncryptionKey. See where we throw the KeyringControllerError.ExpiredCredentials error.

Though... it is a bit confusing that the guarantee about these being the same is in a different function 🤔. Perhaps it would be simplest to use this.#encryptionKey.salt; just so the reader doesn't need to understand where it was validated?

Potentially a slight readability improvement here, but I don't think it's a bug

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense. I was being conservative on what was there before, but I agree that using this.#encryptionKey.salt is clearer, and should be equivalent. Done in c59a9c1

Base automatically changed from mikesposito/keyring-controller/remove-cache-encryption-key to main November 12, 2025 14:33
@mikesposito mikesposito force-pushed the mikesposito/keyring-controller/never-use-password branch from 5f8200d to 54baecf Compare November 12, 2025 18:33
@mikesposito mikesposito marked this pull request as ready for review November 12, 2025 18:36
@mikesposito mikesposito requested review from a team as code owners November 12, 2025 18:36
@socket-security
Copy link

socket-security bot commented Nov 12, 2025

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Updated@​metamask/​browser-passworder@​4.3.0 ⏵ 6.0.010010010084 +3100

View full report

@mikesposito
Copy link
Member Author

@metamaskbot publish-preview

@github-actions
Copy link
Contributor

Preview builds have been published. See these instructions for more information about preview builds.

Expand for full list of packages and versions.
{
  "@metamask-previews/account-tree-controller": "3.0.0-preview-20df3e99",
  "@metamask-previews/accounts-controller": "34.0.0-preview-20df3e99",
  "@metamask-previews/address-book-controller": "7.0.0-preview-20df3e99",
  "@metamask-previews/analytics-controller": "0.0.0-preview-20df3e99",
  "@metamask-previews/announcement-controller": "8.0.0-preview-20df3e99",
  "@metamask-previews/app-metadata-controller": "2.0.0-preview-20df3e99",
  "@metamask-previews/approval-controller": "8.0.0-preview-20df3e99",
  "@metamask-previews/assets-controllers": "88.0.0-preview-20df3e99",
  "@metamask-previews/base-controller": "9.0.0-preview-20df3e99",
  "@metamask-previews/bridge-controller": "60.1.0-preview-20df3e99",
  "@metamask-previews/bridge-status-controller": "60.1.0-preview-20df3e99",
  "@metamask-previews/build-utils": "3.0.4-preview-20df3e99",
  "@metamask-previews/chain-agnostic-permission": "1.2.2-preview-20df3e99",
  "@metamask-previews/claims-controller": "0.2.0-preview-20df3e99",
  "@metamask-previews/composable-controller": "12.0.0-preview-20df3e99",
  "@metamask-previews/controller-utils": "11.15.0-preview-20df3e99",
  "@metamask-previews/core-backend": "4.0.0-preview-20df3e99",
  "@metamask-previews/delegation-controller": "1.0.0-preview-20df3e99",
  "@metamask-previews/earn-controller": "10.0.0-preview-20df3e99",
  "@metamask-previews/eip-5792-middleware": "2.0.0-preview-20df3e99",
  "@metamask-previews/eip-7702-internal-rpc-middleware": "0.1.0-preview-20df3e99",
  "@metamask-previews/eip1193-permission-middleware": "1.0.2-preview-20df3e99",
  "@metamask-previews/ens-controller": "18.0.0-preview-20df3e99",
  "@metamask-previews/error-reporting-service": "3.0.0-preview-20df3e99",
  "@metamask-previews/eth-block-tracker": "14.0.0-preview-20df3e99",
  "@metamask-previews/eth-json-rpc-middleware": "21.0.0-preview-20df3e99",
  "@metamask-previews/eth-json-rpc-provider": "5.0.1-preview-20df3e99",
  "@metamask-previews/foundryup": "1.0.1-preview-20df3e99",
  "@metamask-previews/gas-fee-controller": "25.0.0-preview-20df3e99",
  "@metamask-previews/gator-permissions-controller": "0.4.0-preview-20df3e99",
  "@metamask-previews/json-rpc-engine": "10.1.1-preview-20df3e99",
  "@metamask-previews/json-rpc-middleware-stream": "8.0.8-preview-20df3e99",
  "@metamask-previews/keyring-controller": "24.0.0-preview-20df3e99",
  "@metamask-previews/logging-controller": "7.0.0-preview-20df3e99",
  "@metamask-previews/message-manager": "14.0.0-preview-20df3e99",
  "@metamask-previews/messenger": "0.3.0-preview-20df3e99",
  "@metamask-previews/multichain-account-service": "3.0.0-preview-20df3e99",
  "@metamask-previews/multichain-api-middleware": "1.2.4-preview-20df3e99",
  "@metamask-previews/multichain-network-controller": "2.0.0-preview-20df3e99",
  "@metamask-previews/multichain-transactions-controller": "6.0.0-preview-20df3e99",
  "@metamask-previews/name-controller": "9.0.0-preview-20df3e99",
  "@metamask-previews/network-controller": "25.0.0-preview-20df3e99",
  "@metamask-previews/network-enablement-controller": "3.1.0-preview-20df3e99",
  "@metamask-previews/notification-services-controller": "19.0.0-preview-20df3e99",
  "@metamask-previews/permission-controller": "12.1.0-preview-20df3e99",
  "@metamask-previews/permission-log-controller": "5.0.0-preview-20df3e99",
  "@metamask-previews/phishing-controller": "15.0.0-preview-20df3e99",
  "@metamask-previews/polling-controller": "15.0.0-preview-20df3e99",
  "@metamask-previews/preferences-controller": "21.0.0-preview-20df3e99",
  "@metamask-previews/profile-sync-controller": "26.0.0-preview-20df3e99",
  "@metamask-previews/rate-limit-controller": "7.0.0-preview-20df3e99",
  "@metamask-previews/remote-feature-flag-controller": "2.0.0-preview-20df3e99",
  "@metamask-previews/sample-controllers": "3.0.0-preview-20df3e99",
  "@metamask-previews/seedless-onboarding-controller": "6.1.0-preview-20df3e99",
  "@metamask-previews/selected-network-controller": "25.0.0-preview-20df3e99",
  "@metamask-previews/shield-controller": "2.0.0-preview-20df3e99",
  "@metamask-previews/signature-controller": "36.0.0-preview-20df3e99",
  "@metamask-previews/subscription-controller": "4.2.1-preview-20df3e99",
  "@metamask-previews/token-search-discovery-controller": "4.0.0-preview-20df3e99",
  "@metamask-previews/transaction-controller": "61.2.0-preview-20df3e99",
  "@metamask-previews/transaction-pay-controller": "5.0.0-preview-20df3e99",
  "@metamask-previews/user-operation-controller": "40.0.0-preview-20df3e99"
}

Copy link
Member

@Gudahtt Gudahtt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@mikesposito mikesposito merged commit 777121a into main Nov 17, 2025
272 checks passed
@mikesposito mikesposito deleted the mikesposito/keyring-controller/never-use-password branch November 17, 2025 13:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[KeyringController] Drop uncached encryption support

5 participants